;+
; NAME:
;  STR_SIZE
;
; PURPOSE:
;
;  The purpose of this function is to return the proper
;  character size to make a specified string a specifed
;  width in a window. The width is specified in normalized
;  coordinates. The function is extremely useful for sizing
;  strings and labels in resizeable graphics windows.
;
; AUTHOR:
;
;   FANNING SOFTWARE CONSULTING
;   David Fanning, Ph.D.
;   1645 Sheely Drive
;   Fort Collins, CO 80526 USA
;   Phone: 970-221-0438
;   E-mail: davidf@dfanning.com
;   Coyote's Guide to IDL Programming: http://www.dfanning.com/
;
; CATEGORY:
;
;  Graphics Programs, Widgets.
;
; CALLING SEQUENCE:
;
;  thisCharSize = STR_SIZE(thisSting, targetWidth)
;
; INPUTS:
;
;  thisString:  This is the string that you want to make a specifed
;     target size or width.
;
; OPTIONAL INPUTS:
;
;  targetWidth:  This is the target width of the string in normalized
;     coordinates in the current graphics window. The character
;     size of the string (returned as thisCharSize) will be
;     calculated to get the string width as close as possible to
;     the target width. The default is 0.25.
;
; KEYWORD PARAMETERS:
;
;  INITSIZE:  This is the initial size of the string. Default is 1.0.
;
;  STEP:   This is the amount the string size will change in each step
;     of the interative process of calculating the string size.
;     The default value is 0.05.
;
;  XPOS:   X position of the output test string. This can be
;     used on the Postscript device, where no pixmap windows are
;     available and where therefore the test strings would appear on
;     the printable area. Default is 0.5 on most devices. If !D.NAME
;     is PS, the default is 2.0 to draw the test string out of the
;     drawable window area.
;
;  YPOS:   Y position of the output test string. This can be
;     used on the Postscript device, where no pixmap windows are
;     available and where therefore the test strings would appear on
;     the printable area. Default is 0.5 on most devices. If !D.NAME
;     is PS, the default is 2.0 to draw the test string out of the
;     drawable window area.
;
; OUTPUTS:
;
;  thisCharSize:  This is the size the specified string should be set
;     to if you want to produce output of the specified target
;     width. The value is in standard character size units where
;     1.0 is the standard character size.
;
; EXAMPLE:
;
;  To make the string "Happy Holidays" take up 30% of the width of
;  the current graphics window, type this:
;
;      XYOUTS, 0.5, 0.5, ALIGN=0.5, "Happy Holidays", $
;        CHARSIZE=STR_SIZE("Happy Holidays", 0.3)
;
; MODIFICATION HISTORY:
;
;  Written by: David Fanning, 17 DEC 96.
;  Added a scaling factor to take into account the aspect ratio
;     of the window in determing the character size. 28 Oct 97. DWF
;  Added check to be sure hardware fonts are not selected. 29 April 2000. DWF.
;  Added a pixmap to get proper scaling in skinny windows. 16 May 2000. DWF.
;  Forgot I can't do pixmaps in all devices. :-( Fixed. 7 Aug 2000. DWF.
;  Added support of PostScript at behest of Benjamin Hornberger. 11 November 2004. DWF.
;-
;
;******************************************************************************************;
;  Copyright (c) 2008, by Fanning Software Consulting, Inc.                                ;
;  All rights reserved.                                                                    ;
;                                                                                          ;
;  Redistribution and use in source and binary forms, with or without                      ;
;  modification, are permitted provided that the following conditions are met:             ;
;                                                                                          ;
;      * Redistributions of source code must retain the above copyright                    ;
;        notice, this list of conditions and the following disclaimer.                     ;
;      * Redistributions in binary form must reproduce the above copyright                 ;
;        notice, this list of conditions and the following disclaimer in the               ;
;        documentation and/or other materials provided with the distribution.              ;
;      * Neither the name of Fanning Software Consulting, Inc. nor the names of its        ;
;        contributors may be used to endorse or promote products derived from this         ;
;        software without specific prior written permission.                               ;
;                                                                                          ;
;  THIS SOFTWARE IS PROVIDED BY FANNING SOFTWARE CONSULTING, INC. ''AS IS'' AND ANY        ;
;  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES    ;
;  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT     ;
;  SHALL FANNING SOFTWARE CONSULTING, INC. BE LIABLE FOR ANY DIRECT, INDIRECT,             ;
;  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED    ;
;  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;         ;
;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND             ;
;  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT              ;
;  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS           ;
;  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                            ;
;******************************************************************************************;
FUNCTION STR_SIZE, theString, targetWidth, $
   INITSIZE=initsize, $
   STEP=step, $
   XPOS=xpos, $
   YPOS=ypos

ON_ERROR, 2

; No hardware fonts.
thisFont = !P.Font
IF (thisFont EQ 0) AND (!D.NAME NE 'PS') THEN !P.Font = -1

; Check positional parameters.
np = N_PARAMS()
CASE np OF
   0: MESSAGE, 'One string parameter is required.'
   1: targetWidth = 0.25
   ELSE:
ENDCASE

; Check keywords. Assign default values.
IF N_ELEMENTS(initsize) EQ 0 THEN initsize = 1.0
IF N_ELEMENTS(step) EQ 0 THEN step = 0.05
IF N_ELEMENTS(orientation) EQ 0 THEN orientation = 0.0 ELSE orientation = Float(orientation)
IF N_ELEMENTS(xpos) EQ 0 THEN IF !D.NAME NE 'PS' THEN xpos = 0.5 ELSE xpos = 2.0
IF N_ELEMENTS(ypos) EQ 0 THEN IF !D.NAME NE 'PS' THEN ypos = 0.5 ELSE ypos = 2.0

   ; Create a pixmap window for drawing the string.

currentWindow = !D.Window

IF !D.X_Size GE !D.Y_Size AND ((!D.Flags AND 256) NE 0) THEN BEGIN
   Window, /Pixmap, /Free, XSize=!D.X_Size, YSize=!D.Y_Size
   pixID = !D.Window
ENDIF ELSE BEGIN
   IF ((!D.Flags AND 256) NE 0) THEN BEGIN
      Window, /Pixmap, /Free, XSize=!D.Y_Size, YSize=!D.X_Size
      pixID = !D.Window
   ENDIF
ENDELSE

; Calculate a trial width.
strTrialSize = initsize
XYOUTS, xpos, ypos, ALIGN=0.5, theString, WIDTH=thisWidth, $
      CHARSIZE=-strTrialSize, /NORMAL

   ; Size is perfect.
IF thisWidth EQ targetWidth THEN BEGIN
   !P.Font = thisFont
   theSize = strTrialSize; * Float(!D.Y_Size)/!D.X_Size
   IF currentWindow NE -1 THEN WSet, currentWindow
   IF N_Elements(pixID) NE 0 THEN WDelete, pixID
   RETURN, theSize
ENDIF

   ; Initial size is too big.

IF thisWidth GT targetWidth THEN BEGIN
   REPEAT BEGIN
     XYOUTS, xpos, ypos, ALIGN=0.5, theString, WIDTH=thisWidth, $
        CHARSIZE=-strTrialSize, /NORMAL
     strTrialSize = strTrialSize - step
   ENDREP UNTIL thisWidth LE targetWidth
   !P.Font = thisFont
   theSize = strTrialSize
   IF currentWindow NE -1 THEN WSet, currentWindow
   IF N_Elements(pixID) NE 0 THEN WDelete, pixID
   RETURN, theSize
ENDIF

   ; Initial size is too small.

IF thisWidth LT targetWidth THEN BEGIN
   REPEAT BEGIN
     XYOUTS, xpos, ypos, ALIGN=0.5, theString, WIDTH=thisWidth, $
        CHARSIZE=-strTrialSize, /NORMAL
     strTrialSize = strTrialSize + step
   ENDREP UNTIL thisWidth GT targetWidth
   strTrialSize = strTrialSize - step ; Need a value slightly smaller than target.
   !P.Font = thisFont
   theSize = strTrialSize
   IF currentWindow NE -1 THEN WSet, currentWindow
   IF N_Elements(pixID) NE 0 THEN WDelete, pixID
   RETURN, theSize
ENDIF

   ; Cleanup.

!P.Font = thisFont

END